<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>简单语音识别 - 自动传输</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 20px;
        }

        .container {
            background: white;
            border-radius: 20px;
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
            padding: 40px;
            max-width: 600px;
            width: 100%;
        }

        h1 {
            color: #333;
            margin-bottom: 30px;
            text-align: center;
        }

        .status {
            padding: 15px;
            border-radius: 10px;
            margin-bottom: 20px;
            text-align: center;
            font-weight: 500;
            transition: all 0.3s;
        }

        .status.connected {
            background: #c6f6d5;
            color: #22543d;
        }

        .status.disconnected {
            background: #fed7d7;
            color: #742a2a;
        }

        .status.recording {
            background: #fef3cd;
            color: #744210;
            animation: pulse 1.5s infinite;
        }

        @keyframes pulse {
            0%, 100% { opacity: 1; }
            50% { opacity: 0.7; }
        }

        .controls {
            display: flex;
            gap: 15px;
            margin-bottom: 30px;
        }

        button {
            flex: 1;
            padding: 15px;
            border: none;
            border-radius: 10px;
            font-size: 16px;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s;
        }

        button:disabled {
            opacity: 0.5;
            cursor: not-allowed;
        }

        .btn-connect {
            background: #667eea;
            color: white;
        }

        .btn-connect:hover:not(:disabled) {
            background: #5568d3;
        }

        .btn-record {
            background: #48bb78;
            color: white;
        }

        .btn-record:hover:not(:disabled) {
            background: #38a169;
        }

        .btn-recording {
            background: #f56565;
            animation: recording 1.5s infinite;
        }

        @keyframes recording {
            0%, 100% { 
                box-shadow: 0 0 0 0 rgba(245, 101, 101, 0.4);
            }
            50% { 
                box-shadow: 0 0 0 10px rgba(245, 101, 101, 0);
            }
        }

        .result-box {
            background: #f7fafc;
            border: 2px solid #e2e8f0;
            border-radius: 10px;
            padding: 20px;
            min-height: 200px;
            margin-bottom: 20px;
        }

        .result-box h3 {
            color: #2d3748;
            margin-bottom: 10px;
            font-size: 16px;
        }

        .result-text {
            color: #4a5568;
            line-height: 1.8;
            white-space: pre-wrap;
            word-wrap: break-word;
            max-height: 300px;
            overflow-y: auto;
        }

        .result-text:empty::before {
            content: "等待识别...";
            color: #a0aec0;
            font-style: italic;
        }

        /* 音量指示器 */
        .volume-meter {
            height: 30px;
            background: #e2e8f0;
            border-radius: 15px;
            margin-bottom: 20px;
            overflow: hidden;
            position: relative;
        }

        .volume-level {
            height: 100%;
            background: linear-gradient(90deg, #48bb78 0%, #f6ad55 70%, #f56565 100%);
            border-radius: 15px;
            width: 0%;
            transition: width 0.1s;
        }

        .volume-label {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            color: #4a5568;
            font-size: 12px;
            font-weight: 600;
        }

        .stats {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 15px;
            margin-bottom: 20px;
        }

        .stat {
            text-align: center;
            padding: 10px;
            background: #edf2f7;
            border-radius: 8px;
        }

        .stat-value {
            font-size: 20px;
            font-weight: bold;
            color: #667eea;
        }

        .stat-label {
            font-size: 12px;
            color: #718096;
            margin-top: 5px;
        }

        .config {
            background: #edf2f7;
            border-radius: 10px;
            padding: 15px;
            margin-bottom: 20px;
        }

        .config-item {
            margin-bottom: 10px;
        }

        .config-item label {
            display: block;
            color: #4a5568;
            font-size: 14px;
            margin-bottom: 5px;
        }

        .config-item input {
            width: 100%;
            padding: 8px;
            border: 1px solid #cbd5e0;
            border-radius: 5px;
            font-size: 14px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>🎤 简单语音识别</h1>
        
        <div class="config">
            <div class="config-item">
                <label for="wsUrl">WebSocket 地址</label>
                <input type="text" id="wsUrl" value="ws://localhost:48090/ws">
            </div>
        </div>

        <div id="status" class="status disconnected">
            ⚠️ 未连接
        </div>

        <div class="controls">
            <button id="connectBtn" class="btn-connect">连接</button>
            <button id="recordBtn" class="btn-record" disabled>开始录音</button>
        </div>

        <!-- 音量指示器 -->
        <div class="volume-meter">
            <div class="volume-level" id="volumeLevel"></div>
            <span class="volume-label" id="volumeLabel">音量: 0%</span>
        </div>

        <!-- 统计信息 -->
        <div class="stats">
            <div class="stat">
                <div class="stat-value" id="duration">0s</div>
                <div class="stat-label">录音时长</div>
            </div>
            <div class="stat">
                <div class="stat-value" id="chunks">0</div>
                <div class="stat-label">发送次数</div>
            </div>
            <div class="stat">
                <div class="stat-value" id="words">0</div>
                <div class="stat-label">识别字数</div>
            </div>
        </div>

        <!-- 识别结果 -->
        <div class="result-box">
            <h3>识别结果</h3>
            <div class="result-text" id="resultText"></div>
        </div>
    </div>

    <script>
        // 全局变量
        let ws = null;
        let mediaRecorder = null;
        let audioStream = null;
        let isRecording = false;
        let audioContext = null;
        let analyser = null;
        let scriptProcessor = null;
        let recordingStartTime = null;
        let chunksSent = 0;
        let totalWords = 0;
        let silenceTimer = null;
        let audioBuffer = [];

        // 配置
        const SILENCE_THRESHOLD = 0.01;  // 静音阈值
        const SILENCE_DURATION = 500;    // 静音持续时间（毫秒）
        const CHUNK_INTERVAL = 1500;      // 发送间隔（毫秒）
        
        // DOM 元素
        const statusEl = document.getElementById('status');
        const connectBtn = document.getElementById('connectBtn');
        const recordBtn = document.getElementById('recordBtn');
        const resultText = document.getElementById('resultText');
        const volumeLevel = document.getElementById('volumeLevel');
        const volumeLabel = document.getElementById('volumeLabel');

        // 连接 WebSocket
        function connect() {
            if (ws && ws.readyState === WebSocket.OPEN) {
                disconnect();
                return;
            }

            const wsUrl = document.getElementById('wsUrl').value;
            ws = new WebSocket(wsUrl);

            ws.onopen = () => {
                updateStatus('connected', '✅ 已连接');
                connectBtn.textContent = '断开';
                recordBtn.disabled = false;
                console.log('WebSocket 已连接');
            };

            ws.onmessage = (event) => {
                try {
                    const message = JSON.parse(event.data);
                    if (message.type === 'simple-voice') {
                        const content = JSON.parse(message.content);
                        if (content.text && content.text.trim()) {
                            appendText(content.text);
                        }
                    }
                } catch (e) {
                    console.error('消息解析失败:', e);
                }
            };

            ws.onerror = (error) => {
                console.error('WebSocket 错误:', error);
                updateStatus('disconnected', '⚠️ 连接错误');
            };

            ws.onclose = () => {
                updateStatus('disconnected', '⚠️ 未连接');
                connectBtn.textContent = '连接';
                recordBtn.disabled = true;
                if (isRecording) {
                    stopRecording();
                }
            };
        }

        // 断开连接
        function disconnect() {
            if (ws) {
                ws.close();
                ws = null;
            }
        }

        // 更新状态
        function updateStatus(className, text) {
            statusEl.className = 'status ' + className;
            statusEl.textContent = text;
        }

        // 开始录音
        async function startRecording() {
            try {
                // 获取麦克风权限
                audioStream = await navigator.mediaDevices.getUserMedia({ 
                    audio: {
                        echoCancellation: true,
                        noiseSuppression: true,
                        autoGainControl: true,
                        sampleRate: 16000
                    }
                });

                // 创建音频上下文
                audioContext = new AudioContext({ sampleRate: 16000 });
                const source = audioContext.createMediaStreamSource(audioStream);
                
                // 创建分析器（用于音量检测）
                analyser = audioContext.createAnalyser();
                analyser.fftSize = 256;
                source.connect(analyser);

                // 创建 MediaRecorder
                const mimeType = MediaRecorder.isTypeSupported('audio/webm;codecs=opus') 
                    ? 'audio/webm;codecs=opus' 
                    : 'audio/webm';
                
                mediaRecorder = new MediaRecorder(audioStream, {
                    mimeType: mimeType,
                    audioBitsPerSecond: 16000
                });

                // 处理音频数据
                mediaRecorder.ondataavailable = (event) => {
                    if (event.data.size > 0) {
                        audioBuffer.push(event.data);
                    }
                };

                // 开始录音，每 CHUNK_INTERVAL 毫秒产生一次数据
                mediaRecorder.start(CHUNK_INTERVAL);

                // 定期处理和发送音频
                startAudioProcessing();

                // 更新 UI
                isRecording = true;
                recordingStartTime = Date.now();
                recordBtn.textContent = '停止录音';
                recordBtn.classList.add('btn-recording');
                updateStatus('recording', '🔴 录音中...');
                
                // 开始音量监测
                monitorVolume();
                
                // 更新时长
                updateDuration();

                console.log('开始录音');

            } catch (error) {
                console.error('录音失败:', error);
                alert('无法访问麦克风：' + error.message);
            }
        }

        // 定期处理音频数据
        function startAudioProcessing() {
            const processInterval = setInterval(() => {
                if (!isRecording) {
                    clearInterval(processInterval);
                    return;
                }

                // 如果有累积的音频数据，发送到服务器
                if (audioBuffer.length > 0) {
                    const blob = new Blob(audioBuffer, { type: 'audio/webm' });
                    audioBuffer = []; // 清空缓冲区
                    
                    // 只有当音频块足够大时才发送（避免发送空音频）
                    if (blob.size > 100) {
                        sendAudioData(blob);
                    }
                }
            }, 1000); // 每秒处理一次
        }

        // 发送音频数据
        function sendAudioData(blob) {
            if (!ws || ws.readyState !== WebSocket.OPEN) {
                console.warn('WebSocket 未连接');
                return;
            }

            const reader = new FileReader();
            reader.onloadend = () => {
                const base64data = reader.result;
                
                // 简单直接发送音频数据
                ws.send(JSON.stringify({
                    type: 'simple-voice',
                    content: JSON.stringify({
                        audio: base64data,
                        language: 'zh'
                    })
                }));

                chunksSent++;
                document.getElementById('chunks').textContent = chunksSent;
                console.log(`发送音频块 #${chunksSent}, 大小: ${blob.size} bytes`);
            };
            reader.readAsDataURL(blob);
        }

        // 停止录音
        function stopRecording() {
            if (mediaRecorder && isRecording) {
                mediaRecorder.stop();
                
                // 发送最后的音频数据
                if (audioBuffer.length > 0) {
                    const blob = new Blob(audioBuffer, { type: 'audio/webm' });
                    sendAudioData(blob);
                    audioBuffer = [];
                }

                // 停止音频流
                if (audioStream) {
                    audioStream.getTracks().forEach(track => track.stop());
                }

                // 清理音频上下文
                if (audioContext) {
                    audioContext.close();
                }

                // 更新 UI
                isRecording = false;
                recordBtn.textContent = '开始录音';
                recordBtn.classList.remove('btn-recording');
                updateStatus('connected', '✅ 已连接');

                console.log('停止录音');
            }
        }

        // 监测音量
        function monitorVolume() {
            if (!analyser || !isRecording) return;

            const dataArray = new Uint8Array(analyser.frequencyBinCount);
            analyser.getByteFrequencyData(dataArray);

            // 计算平均音量
            let sum = 0;
            for (let i = 0; i < dataArray.length; i++) {
                sum += dataArray[i];
            }
            const average = sum / dataArray.length;
            const volumePercent = Math.min(100, Math.round((average / 255) * 200));

            // 更新音量显示
            volumeLevel.style.width = volumePercent + '%';
            volumeLabel.textContent = `音量: ${volumePercent}%`;

            // 继续监测
            if (isRecording) {
                requestAnimationFrame(monitorVolume);
            }
        }

        // 更新录音时长
        function updateDuration() {
            if (isRecording && recordingStartTime) {
                const duration = Math.floor((Date.now() - recordingStartTime) / 1000);
                document.getElementById('duration').textContent = duration + 's';
                setTimeout(updateDuration, 1000);
            }
        }

        // 追加识别文本
        function appendText(text) {
            resultText.textContent += text + ' ';
            totalWords += text.length;
            document.getElementById('words').textContent = totalWords;
            
            // 自动滚动
            resultText.scrollTop = resultText.scrollHeight;
        }

        // 清空结果
        function clearResult() {
            resultText.textContent = '';
            totalWords = 0;
            chunksSent = 0;
            document.getElementById('words').textContent = '0';
            document.getElementById('chunks').textContent = '0';
            document.getElementById('duration').textContent = '0s';
        }

        // 事件绑定
        connectBtn.addEventListener('click', connect);
        
        recordBtn.addEventListener('click', () => {
            if (isRecording) {
                stopRecording();
            } else {
                clearResult(); // 开始新录音时清空
                startRecording();
            }
        });

        // 页面卸载时清理
        window.addEventListener('beforeunload', () => {
            stopRecording();
            disconnect();
        });

        // 初始化
        console.log('页面已加载');
    </script>
</body>
</html>
